<template>
  <div class="cropper-container">
    <div class="cropper-content">
      <div class="cropper">
        <vue-cropper 
          ref="cropper"
          :img="options.img" 
          :outputType="options.outputType" 
          :autoCrop="options.autoCrop" 
          :full="options.full" 
          :fixed="options.fixed" 
          :fixedNumber="options.fixedNumber" 
          :centerBox="options.centerBox" 
          :mode="options.mode" 
          :high="options.high" 
          :infoTrue="options.infoTrue" 
          :outputSize="options.outputSize" 
          :enlarge="options.enlarge"
          @realTime="onRealTime" 
          @imgLoad="onImgLoad"
        />
      </div>
      <!-- 预览 -->
      <div class="cropper-view">
        <!-- 尺寸列表 -->
        <el-radio-group v-model="fixRadio" :disabled="!switchFix" @change="onFixRadio">
          <el-radio 
            v-for="item in fixRadioList" 
            :key="item.label" 
            :label="item.label"
          >{{ item.text }}</el-radio>
        </el-radio-group>
        <!-- 预览 -->
        <div class="cropper-preview">
          <div :style="previewStyle">
            <img :style="previews.img" :src="previews.url" alt="图片预览">
          </div>
        </div>
      </div>
    </div>
    <div class="cropper-btn">
      <!-- 调整按钮 -->
      <div>
        <el-tooltip effect="dark" placement="top" content="放大">
          <el-button plain circle icon="el-icon-plus" @click="onChangeScale(1)"></el-button>
        </el-tooltip>
        <el-tooltip effect="dark" placement="top" content="放小">
          <el-button plain circle icon="el-icon-minus" @click="onChangeScale(-1)"></el-button>
        </el-tooltip>
        <el-tooltip effect="dark" placement="top" content="左旋转">
          <el-button plain circle icon="el-icon-refresh-left" @click="onRotateLeft"></el-button>
        </el-tooltip>
        <el-tooltip effect="dark" placement="top" content="右旋转">
          <el-button plain circle icon="el-icon-refresh-right" @click="onRotateRight"></el-button>
        </el-tooltip>
      </div>
      <!-- 上传组 -->
      <div class="upload-btn">
        <label class="label-btn" for="uploads">
          <i class="el-icon-folder-add"></i>
          <span>选择图片</span>
        </label>
        <input 
          ref="uploads"
          type="file" 
          id="uploads" 
          style="position:absolute; clip:rect(0 0 0 0);" 
          accept="image/*" 
          @change="onChooseImage($event)" 
        />
        <el-button 
          type="primary" 
          icon="el-icon-upload" 
          :disabled="!options.img" 
          @click="onCrop"
        >裁剪上传</el-button>
        <el-button 
          type="danger" 
          icon="el-icon-upload" 
          :disabled="!options.img" 
          @click="onNotCrop"
        >直接上传</el-button>
      </div>
    </div>
  </div>
</template>

<script>
// 图片裁剪组件
import { VueCropper } from 'vue-cropper'
import { checkFileType, checkFileSize } from '@/utils/validate'

export default {
  name: 'Cropper',
  components: { VueCropper },
  props: {
    switchFix: { // 尺寸是否可切换
      type: Boolean,
      default: true
    },
    fixNumber: { // 截图框的宽高比例 根据比列自动生成尺寸单选框
      type: Array,
      default: () => [1,1]
    },
    resultType: { // 裁剪返回类型 blob数据 base64数据
      type: String,
      default: 'blob'
    },
    // 文件类型
    fileTypes: {
      type: Array,
      default: () => ['jpg','png','jpeg','webp','gif']
    },
    // 文件大小限制(Mb)
    fileSize: {
      type: Number,
      default: 1
    },
  },
  data() {
    return {
      // 初始化参数
      options: {
        img: '',  // 裁剪图片的地址 url 地址 || base64 || blob
        outputType: 'png', // 裁剪生成图片的格式 jpeg || png || webp
        autoCrop: true, // 是否默认生成截图框
        fixed: true, // 是否开启截图框宽高固定比例
        fixedNumber: this.fixNumber, // 截图框的宽高比例
        centerBox: true, // 截图框是否被限制在图片里面
        mode: 'contain', // 图片默认渲染方式 contain , cover, 100px, 100% auto
        full: true,       // 是否输出原图比例的截图 默认false
        high: true,       // 是否按照设备的dpr 输出等比例图片 默认true
        infoTrue: true,   // true图片大小信息 false截图狂大小信息 默认false
        outputSize: 1,    // 裁剪生成图片的质量 0.1 - 1 默认1
        enlarge: 1,       // 图片根据截图框输出比例倍数 默认1 不要太大
      },
      // 实时预览
      previews: {},
      previewStyle: {},
      // 上传文件名
      fileName: '',
      // 尺寸默认选中
      fixRadio: '1:1',
      fixRadioList: [
        { label: '1:1', text: '1:1' },
        { label: '16:9', text: '16:9' },
        { label: '0', text: '自定义' },
      ]
    }
  },
  created() {
    this.formatRadioList()
  },
  methods: {
    // 初始化尺寸列表
    formatRadioList() {
      // fixNumber不存在默认尺寸中的话 就新增
      const fixStr = this.fixNumber.join(':')
      const index = this.fixRadioList.findIndex(v => v.label === fixStr)
      if (index === -1) {
        this.fixRadioList.unshift({ label: fixStr, text: fixStr })
      }
      this.fixRadio = fixStr
    },
    // 放大/缩小 正数为变大 负数变小
    onChangeScale(num) {
      num = num || 1
      this.$refs.cropper.changeScale(num)
    },
    // 向左边旋转90度
    onRotateLeft() {
      this.$refs.cropper.rotateLeft()
    },
    // 向右边旋转90度
    onRotateRight() {
      this.$refs.cropper.rotateRight()
    },
    // 选择本地图片
    onChooseImage(event) {
      const { files} = event.target
      const file = files[0]
      // 截取文件名 + 自定义的后缀
      this.fileName = file.name.slice(0, file.name.lastIndexOf('.')) + '.' + this.options.outputType
      // 校检格式
      if (!checkFileType(file, 'image', this.fileTypes)) {
        this.$notify.warning({
          title: "警告",
          message: `请上传${this.fileTypes.join('/')}格式图片`,
          duration: 2000,
        });
        this.$refs.uploads.value = null
        return false
      }
      // 校检文件大小
      if (!checkFileSize(file, this.fileSize)) {
        this.$notify.warning({
          title: "警告",
          message: `请上传${this.fileSize}MB以内图片`,
          duration: 2000,
        });
        this.$refs.uploads.value = null
        return false 
      }
      this.file = file
      const reader = new FileReader()
      reader.onload = (e) => {
        const { result } = e.target
        if (typeof result === 'object') { 
          // 把Array Buffer转化为blob
          this.options.img = window.URL.createObjectURL(new Blob([result])) 
        } else {
          // 如果是base64不需要
          this.options.img = result
        }
      }
      // 转化为base64 
      // reader.readAsDataURL(file)
      // 转化为blob 
      reader.readAsArrayBuffer(file) 
    },
    // 裁剪
    onCrop() {
      if (this.resultType === 'blob') {
        // 获取截图的blob数据
        this.$refs.cropper.getCropBlob((data) => {
          this.$emit('cropFinish', {
            fileType: this.resultType,
            fileName: this.fileName,
            fileSuffix: `.${this.options.outputType}`,
            data: data
          })
        })
      } else {
        // 获取截图的base64 数据
        this.$refs.cropper.getCropData((data) => {
          this.$emit('cropFinish', {
            fileType: this.resultType,
            fileName: this.fileName,
            fileSuffix: `.${this.options.outputType}`,
            data: data
          })
        })
      }
    },
    // 不裁剪 直接上传
    onNotCrop() {
      this.$emit('notCropFinish', {
        file: this.file
      })
    },
    // 切换尺寸
    onFixRadio(value) {
      if (value == '0') {
        this.options.fixed = false
      } else {
        this.options.fixed = true
        this.options.fixedNumber = value.split(':')
        this.$nextTick(() => {
          // 重绘截图狂
          this.$refs.cropper.goAutoCrop()
        })
      }
    },
    // 实时预览函数
    onRealTime(data) {
      this.previewStyle = {
        width: data.w + "px",
        height: data.h + "px",
        overflow: "hidden",
        margin: "0 auto 0",
        zoom: 200 / data.w
      };
      this.previews = data
    },
    // 图片加载的回调
    onImgLoad(msg) {
      if (msg === 'error') {
        this.$notify.error({
          title: "错误",
          message: '图片加载失败 换张图片试试',
          duration: 2000,
        });
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.cropper-container {
  min-width: 700px;
  .cropper-content {
    display: flex;
    .cropper {
      width: 400px;
      height: 300px;
    }
    .cropper-view {
      width: 300px;
      text-align: center;
      .cropper-preview {
        padding-top: 30px;
      }
    }
  }
  .cropper-btn {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 25px;
    .upload-btn {
      .label-btn {
        display: inline-block;
        line-height: 1;
        white-space: nowrap;
        cursor: pointer;
        text-align: center;
        box-sizing: border-box;
        transition: .1s;
        font-weight: 500;
        -moz-user-select: none;
        -webkit-user-select: none;
        -ms-user-select: none;
        user-select: none;
        padding: 12px 20px;
        margin-right: 10px;
        font-size: 14px;
        border-radius: 4px;
        border: 1px solid #07c160;
        color: #fff;
        background-color: #07c160;
        &:hover {
          background-color: #39cd80;
        }
        span {
          margin-left: 5px;
        }
      }
    }
  }
}

</style>
